let {
	x,
	y,
	...z
} = {
	x: 1,
	y: 2,
	a: 3,
	b: 4
};
x // 1
y // 2
z // { a: 3, b: 4 }


//  null或者undefined 或者结赋值时不是最后一个值都会报错
let { ...z
} = null; // 运行时错误

let { ...z
} = undefined; // 运行时错误

let { ...x,
	y,
	z
} = someObject; // 句法错误

let {
	x,
	...y,
	...z
} = someObject; // 句法错误


//注意浅拷贝
let obj = {
	a: {
		b: 1
	}
};
let { ...x
} = obj;
obj.a.b = 2;
x.a.b // 2


//另外，扩展运算符的解构赋值，不能复制继承自原型对象的属性。
let o1 = {
	a: 1
};
let o2 = {
	b: 2
};
o2.__proto__ = o1;
let { ...o3
} = o2;
o3 // { b: 2 }
o3.a // undefined


const o = Object.create({
	x: 1,
	y: 2
});
o.z = 3;

let {
	x, //不是扩展运算符可以读取该对象的proto
	...newObj //是扩展运算符 所以无法读取该对象的proto的y的值
} = o;
let {
	y,
	z
} = newObj;
x // 1
y // undefined
z // 3


function baseFunction({
	a,
	b
}) {
	// ...
}

function wrapperFunction({
	x,
	y,
	...restConfig
}) {
	// 使用 x 和 y 参数进行操作
	// 其余参数传给原始函数
	return baseFunction(restConfig);
}


//对象的扩展运算符（...）用于取出参数对象的所有可遍历属性， 拷贝到当前对象之中。可利用此拷贝对象,但也要注意浅拷贝问题
let z = {
	a: 3,
	b: 4
};
let n = { ...z
};
n // { a: 3, b: 4 }



//由于数组是特殊的对象， 所以对象的扩展运算符也可以用于数组。
let foo = { ...['a', 'b', 'c']
};
foo
// {0: "a", 1: "b", 2: "c"}


//如果扩展运算符后面是一个空对象， 则没有任何效果。
{ ...{}, a: 1
}
// { a: 1 }


//如果扩展运算符后面不是对象， 则会自动将其转为对象。
// 等同于 {...Object(1)}
{ ...1
} // {}

// 等同于 {...Object(true)}
{ ...true
} // {}

// 等同于 {...Object(undefined)}
{ ...undefined
} // {}

// 等同于 {...Object(null)}
{ ...null
} // {}

//但是， 如果扩展运算符后面是字符串， 它会自动转成一个类似数组的对象， 因此返回的不是空对象。
{ ...'hello'
}
// {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}



//对象的扩展运算符等同于使用Object.assign()方法。
let aClone = { ...a
};
// 等同于
let aClone = Object.assign({}, a);



/*

如果想完整克隆一个对象， 还拷贝对象原型的属性， 可以采用下面的写法。
写法一的__proto__属性在非浏览器的环境不一定部署，因此推荐使用写法二和写法三。

*/
// 写法一
const clone1 = {
	__proto__: Object.getPrototypeOf(obj),
	...obj
};

// 写法二
const clone2 = Object.assign(
	Object.create(Object.getPrototypeOf(obj)),
	obj
);

// 写法三  这个好理解一些
const clone3 = Object.create(
	Object.getPrototypeOf(obj),
	Object.getOwnPropertyDescriptors(obj)
)


//扩展运算符可以用于合并两个对象。
let ab = { ...a,
	...b
};
// 等同于
let ab = Object.assign({}, a, b);


//如果用户自定义的属性， 放在扩展运算符后面， 则扩展运算符内部的同名属性会被覆盖掉。
let aWithOverrides = { ...a,
	x: 1,
	y: 2
};
// 等同于
let aWithOverrides = { ...a,
	...{
		x: 1,
		y: 2
	}
};
// 等同于
let x = 1,
	y = 2,
	aWithOverrides = { ...a,
		x,
		y
	};
// 等同于
let aWithOverrides = Object.assign({}, a, {
	x: 1,
	y: 2
});


//与数组的扩展运算符一样， 对象的扩展运算符后面可以跟表达式。
const obj = {
	...(x > 1 ? {
		a: 1
	} : {}),
	b: 2,
};


//扩展运算符的参数对象之中，如果有取值函数get，这个函数是会执行的。
let a = {
	get x() {
		throw new Error('not throw yet');
	}
}

let aWithXGetter = { ...a
}; // 报错
